// SINGULARITY HTML5 GAME ENGINE
// -----------------------------
// Copyright(c) 2013 by Codenova.pl

define(['../Core', '../Shapes/Circle'], function (Core, Circle) {
    var Particles = function(objParticles) {
        this.Amount = objParticles.Amount;
        this.ActiveElements = {};
        this.Elements = [];
        this.ElementsToRemove = {};
        this.OpacityBottomLimit = 0.5;
        this.Radius = objParticles.Radius;
        this.RemovalLimit = objParticles.RemovalLimit;
        this.TTL = objParticles.TTL / 1000;
    };

    Particles.prototype = {
        Add: function(canvas, ctx) {
            var t = this;

            // Save set of particles
            this.AddParticles(canvas);

            // Start animation
            self.requestAnimationFrame(function() {
                t.DrawParticles(canvas, ctx);
            });
        },

        AddParticles: function(canvas) {
            var _i, _length;

            for(_i = 0, _length = this.Amount - 1; _i <= _length; _i = _i + 1) {
                this.Elements.push(Circle({
                    Background: '255, 255, 255',
                    Opacity: 0,
                    Radius: this.Radius,
                    X: Core.GenerateRandomInteger(0, canvas.width),
                    Y: Core.GenerateRandomInteger(0, 500)
                }));
            }
        },

        Create: function(objParticles) {
            "use strict";
            return new Particles(objParticles);
        },

        DrawParticles: function(canvas, ctx) {
            var t = this;

            // Clear stage
            Core.ClearCanvas(ctx, canvas.width, canvas.height);

            // Random element and set it for drawing
            if(Object.keys(this.ActiveElements).length < this.RemovalLimit) {
                // Random number for element
                var randomElementNumber = Core.GenerateRandomInteger(0, this.Elements.length - 1);

                // Check if element exists, don't try to add another if so
                if(typeof this.ActiveElements[this.Elements[randomElementNumber].Id] === "undefined" && typeof this.ElementsToRemove[this.Elements[randomElementNumber].Id] === "undefined" ) {
                    // Add element to our drawing array
                    this.ActiveElements[this.Elements[randomElementNumber].Id] = this.Elements[randomElementNumber];
                }
            }

            // Draw all stored elements
            for(var activeElement in this.ActiveElements) {
                if(this.ActiveElements.hasOwnProperty(activeElement)) {
                    // We could start removing active elements when they reach full opacity,
                    // but saving them makes particles more random
                    this.ActiveElements[activeElement].Opacity += this.TTL;
                    this.ActiveElements[activeElement].Add(ctx);
                }
            }

            // If we reach removal limit we can start moving elements
            // to "removal" array
            if(Object.keys(this.ActiveElements).length == this.RemovalLimit) {
                var randomElementToRemove = Core.GenerateRandomInteger(0, Object.keys(this.ActiveElements).length - 1);
                var objToRemove = this.ActiveElements[Object.keys(this.ActiveElements)[randomElementToRemove]];

                // Move element if none exists in removal array
                // Additionally element has to reach specific opacity level
                if(typeof this.ElementsToRemove[objToRemove.Id] === "undefined" && objToRemove.Opacity >= this.OpacityBottomLimit) {
                    this.ElementsToRemove[objToRemove.Id] = objToRemove;
                    delete this.ActiveElements[Object.keys(this.ActiveElements)[randomElementToRemove]];
                }
            }

            // Start removing elements
            if(Object.keys(this.ElementsToRemove).length > 0) {
                for(var elementToRemove in this.ElementsToRemove) {
                    if(this.ElementsToRemove.hasOwnProperty(elementToRemove)) {
                        // If element's opacity is equal 0, remove it for good
                        if(this.ElementsToRemove[elementToRemove].Opacity > 0) {
                            this.ElementsToRemove[elementToRemove].Opacity -= this.TTL;
                            this.ElementsToRemove[elementToRemove].Add(ctx);
                        } else {
                            delete this.ElementsToRemove[elementToRemove];
                        }
                    }
                }
            }

            // Loop animation
            self.requestAnimationFrame(function() {
                t.DrawParticles(canvas, ctx);
            });
        }
    };

    return Particles.prototype.Create;
});